#pragma once
#include <sys/epoll.h>
#include <unordered_map>
#include "Common.hpp"
#include "Socket.hpp"
using namespace SocketModule;

class EpollServer : public NoCopy
{
private:
    void ListenSocketHandler()
    {
        std::shared_ptr<TCPConnectSocket> connect_socket = _listen_socket->Accept();
        if (!connect_socket)
        { // 检查Accept是否成功
            LOG(LogLevel::ERROR) << "Accept failed";
            return;
        }

        int connect_sockfd = connect_socket->SockFd();
        _connect_socket[connect_sockfd] = connect_socket;

        epoll_event epev;
        epev.events = EPOLLIN;
        epev.data.fd = connect_sockfd;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, connect_sockfd, &epev);
        if (n == -1)
        {
            LOG(LogLevel::FATAL) << "epoll_ctl: 连接套接字注册失败! ";
            exit(EPCTL_ERROR);
        }
    }

    void ConnectSocketHandler(int fd)
    {
        auto it = _connect_socket.find(fd);
        if (it == _connect_socket.end())
        {
            LOG(LogLevel::ERROR) << "Invalid socket fd: " << fd;
            return;
        }

        std::string message;
        int n = it->second->Receive(message);
        std::string client = it->second->Addr().Info();
        if (n > 0)
        {
            // 正常收到消息
            std::cout << "Client[" << client << "] say# " << message << std::endl;
        }
        else if (n == 0)
        {
            // 客户端断开连接
            LOG(LogLevel::INFO) << "Client[" << client << "]已断开连接...";
            // epoll_ctl只能删除合法的fd，所以需要先从epoll中删除，再关闭套接字
            if (epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr) == -1)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl: 套接字关闭失败! ";
            }
            _connect_socket.erase(it);
        }
        else
        {
            // 出错
            LOG(LogLevel::ERROR) << "Receive: 接收Client[" << client << "]的数据失败! ";
            if (epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr) == -1)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl: 套接字关闭失败! ";
            }
            _connect_socket.erase(it);
        }
    }

    void Dispatch(int n)
    {
        for (int i = 0; i < n; i++)
        {
            if (!(_events[i].events & EPOLLIN))
                continue;
            if (_events[i].data.fd == _listen_socket->SockFd())
            {
                // 监听套接字
                ListenSocketHandler();
            }
            else
            {
                // 连接套接字
                ConnectSocketHandler(_events[i].data.fd);
            }
        }
    }

public:
    EpollServer(in_port_t port, size_t size)
        : _isrunning(false), _listen_socket(std::make_shared<TCPListenSocket>(port)), _events(size)
    {
        if (!_listen_socket || _listen_socket->SockFd() < 0)
        {
            LOG(LogLevel::FATAL) << "PollServer: 初始化监听套接字失败! ";
            exit(EXIT_FAILURE);
        }

        // 创建epoll模型
        _epfd = epoll_create(size);
        if (_epfd == -1)
        {
            LOG(LogLevel::FATAL) << "epoll_create: 创建epoll模型失败! ";
            exit(EPCREATE_ERROR);
        }

        // 将监听套接字注册到epoll模型
        int listen_sockfd = _listen_socket->SockFd();
        epoll_event epev;
        epev.events = EPOLLIN;
        epev.data.fd = listen_sockfd;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, listen_sockfd, &epev);
        if (n == -1)
        {
            LOG(LogLevel::FATAL) << "epoll_ctl: 监听套接字注册失败! ";
            exit(EPCTL_ERROR);
        }
    }

    void Run()
    {
        _isrunning = true;
        while (_isrunning)
        {
            int n = epoll_wait(_epfd, _events.data(), _events.size(), 10000);
            if (n < 0)
            {
                // 处理错误，EINTR是可恢复的
                if (errno == EINTR)
                    continue;
                LOG(LogLevel::FATAL) << "poll error: " << strerror(errno);
                break;
            }
            else if (n == 0)
            {
                // 超时
                LOG(LogLevel::DEBUG) << "poll timeout...";
            }
            else
            {
                // 有事件就绪
                LOG(LogLevel::INFO) << "有" << n << "个事件就绪, 即将处理...";
                Dispatch(n);
            }
        }
        _isrunning = false;
    }

    ~EpollServer()
    {
        _isrunning = false;
    }

private:
    bool _isrunning;
    std::shared_ptr<TCPListenSocket> _listen_socket;                            // 监听套接字
    std::unordered_map<int, std::shared_ptr<TCPConnectSocket>> _connect_socket; // 需要poll等待的连接套接字
    int _epfd;                                                                  // epoll模型文件描述符
    std::vector<epoll_event> _events;                                           // epoll事件缓冲区
};